﻿@typeparam TItem where TItem : GridstackChangeEventArgs
@implements IDisposable

<div class="@Class grid-stack" name="@Readonly.ToString()" id="@Options.Id" style="@(_loading?"display:none;":"") @Style">
    @foreach (var item in Items)
    {
        <div class="grid-stack-item"
             id="@item.Id"
             gs-id="@item.Id"
             @key="@item.Id"
             gs-w="@item.Width"
             gs-h="@item.Height"
             gs-x="@item.X"
             gs-y="@item.Y"
             gs-min-w="4"
             gs-min-h="2"
             gs-auto-position="@(IsAutoPosition(item) ?"true":"false")">
            <div class="grid-stack-item-content @ItemClass" style="@ItemStyle">
                @ItemContent(item)
            </div>
        </div>
    }
</div>

@code {
    string? _prevItemKeys;
    bool _prevReadonly;
    bool _loading;

    [Inject]
    public TscGridstackJSModule GridstackJSModule { get; set; }

    [Parameter]
    public string? Style { get; set; }

    [Parameter]
    public string? Class { get; set; }

    [Parameter]
    public string? ItemStyle { get; set; }

    [Parameter]
    public string? ItemClass { get; set; }

    [Parameter]
    public bool Readonly { get; set; }

    [Parameter]
    public string? Id { get; set; }

    [Parameter]
    public Models.GridstackOptions Options { get; set; }

    [Parameter, EditorRequired]
    public ICollection<TItem> Items { get; set; }

    [Parameter, EditorRequired]
    public RenderFragment<TItem> ItemContent { get; set; }

    [Parameter]
    public Func<IEnumerable<GridstackChangeEventArgs>, Task>? OnChange { get; set; }

    [Parameter]
    public Func<IEnumerable<GridstackAddEventArgs>, Task>? OnAdd { get; set; }

    protected override void OnInitialized()
    {
        Options ??= new();
        if (Id is not null) Options.Id = Id;
        GridstackJSModule.OnChangeEvent += OnItemChangeAsync;
        GridstackJSModule.OnAddEvent += OnItemAddAsync;
    }

    async Task OnItemChangeAsync(IEnumerable<GridstackChangeEventArgs> args)
    {
        args.ForEach(arg =>
        {
            var item = Items.FirstOrDefault(e => e.Id == arg.Id);
            if (item is not null)
            {
                item.Width = arg.Width;
                item.Height = arg.Height;
                item.X = arg.X;
                item.Y = arg.Y;
            }
        });
        if (OnChange is not null)
        {
            await OnChange(args);
        }
    }

    async Task OnItemAddAsync(IEnumerable<GridstackAddEventArgs> args)
    {
        if (OnAdd is not null)
        {
            await OnAdd(args);
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _prevItemKeys = string.Join(',', Items.Select(item => item.Id));
            _prevReadonly = Readonly;
            if (Id is null)
            {
                await GridstackJSModule.InitAllAsync(Options);               
            }
            else
            {
                await GridstackJSModule.InitAsync(Options);
            }
            await GridstackJSModule.SwitchStateAsync(!Readonly);
        }
        else
        {
            var itemKeys = string.Join(',', Items.Select(item => item.Id));
            if (_prevItemKeys != itemKeys)
            {
                _prevItemKeys = itemKeys;
                await GridstackJSModule.ReloadAsync();
                await GridstackJSModule.CompactAsync();
                await GridstackJSModule.SaveAsync();
            }
            if (_prevReadonly != Readonly)
            {
                _prevReadonly = Readonly;
                await GridstackJSModule.SwitchStateAsync(!Readonly);
            }
        }
    }

    bool IsAutoPosition(TItem item)
    {
        return item.X == 0 && item.Y == 0 && item.AutoPosition;
    }

    public void Dispose()
    {
        GridstackJSModule.OnChangeEvent -= OnItemChangeAsync;
        GridstackJSModule.OnAddEvent -= OnItemAddAsync;
    }
}
